import random
import string

BANK_INFO = {
    # 银行信息
    "metadata": {
        # 银行名
        "bank_name": "中国工商银行东风路支行",
        # 用户数
        "user_count": 1,
        # 最大用户量
        "max_user_count": 100
    },

    # 用户账号，随机产生8位数
    "12345678": {
        # 姓名
        "name": "用户1",
        # 密码，用户设定6位数
        "password": 123456,
        # 地址
        "address": {
            # 国家
            "country": "中国",
            # 省份
            "province": "湖北省",
            # 街道
            "street": "东风路街道",
            # 门牌号
            "house_num": "幸福小区8栋一单元808"
        },
        # 余额
        "balance": 99999,
        # 开户行，与银行信息保持一致
        "opening_bank": "中国工商银行东风路支行"
    }
}

ERR_CODE = {
    "SUCCESSFUL": 0,
    "AC_NOT_EXIST": 1,
    "AC_EXISTED": 2,
    "AC_COUNT_FULL": 3,
    "PASSWORD_ERR": 4,
    "BALANCE_NOT_ENOUGH": 5
}


def print_err(code: int):
    '''
    根据代号输出对应错误信息
    Args:
        code: 代号

    Returns: None
    '''
    if code == ERR_CODE.get("SUCCESSFUL"):
        print("操作成功！")
    elif code == ERR_CODE.get("AC_NOT_EXIST"):
        print("账号不存在！")
    elif code == ERR_CODE.get("AC_EXISTED"):
        print("账号已存在！")
    elif code == ERR_CODE.get("AC_COUNT_FULL"):
        print("银行账号量已满！")
    elif code == ERR_CODE.get("PASSWORD_ERR"):
        print("密码错误！")
    elif code == ERR_CODE.get("BALANCE_NOT_ENOUGH"):
        print("余额不足！")


def print_menu() -> None:
    """
    打印银行系统的用户界面
    Args: None

    Returns: None
    """
    print("*" * 80)
    print("*", "中国工商银行".center(72, " "), "*")
    print("*", "账户管理系统".center(72, " "), "*")
    print("*", "V1.0".center(76, " "), "*")
    print("*" * 80)
    print("*", "1.开户".center(75, " "), "*")
    print("*", "2.存钱".center(75, " "), "*")
    print("*", "3.取钱".center(75, " "), "*")
    print("*", "4.转账".center(75, " "), "*")
    print("*", "5.查询".center(75, " "), "*")
    print("*", "6.退出".center(75, " "), "*")
    print("*" * 80)


def print_account(account: str) -> None:
    '''
    打印用户信息
    Args:
        account: 用户账号

    Returns: None
    '''
    user = BANK_INFO.get(account)
    address = user.get("address")
    print("*" * 80)
    print("账号: ", account)
    print("密码: ", user.get("password"))
    print("余额: ", user.get("balance"), "￥")
    print("地址: ", f"{address.get('country')} {address.get('province')} "
                    f"{address.get('street')} {address.get('house_num')}")
    print("开户行: ", user.get("opening_bank"))
    print("*" * 80)


def input_int(tip: str) -> int:
    '''
    校验自然数的输入
    Args:
        tip: 输入提示信息

    Returns:
        val：自然整数
    '''
    val = input(tip)
    if val.isdigit() and int(val) > 0:
        return int(val)
    prev_tip = "非自然数！"
    tip = tip if tip.startswith(prev_tip) else prev_tip + tip
    return input_int(tip)


def input_user(account: bool = False, password: bool = False, name: bool = False,
               money: bool = False, address: bool = False, in_account: bool = False) -> dict:
    '''
    通用 用户信息 录入，需要哪些字段传入对应的True即可
    Args:
        account: 账号
        password: 密码
        name: 姓名
        money: 金额
        address: 地址
        in_account: 转入账号

    Returns: dict；录入的信息
    '''

    user = {}
    if account:
        account = input("请输入当前账号: ")
        user["account"] = account
    if password:
        password = input_int("请输入密码（6位数字）: ")
        while len(str(password)) != 6:
            password = input_int("不是6位数！请重新输入: ")
        user["password"] = password
    if name:
        name = input("请输入姓名: ")
        user["name"] = name
    if money:
        money = input_int("请输入金额: ")
        user["money"] = money
    if address:
        print("请输入地址...")
        address = {"country": input("国家: "), "province": input("省份: "),
                   "street": input("街道: "), "house_num": input("门牌号: ")}
        user["address"] = address
    if in_account:
        in_account = input("请输入转入账号: ")
        user["in_account"] = in_account
    return user


def is_exist(account: str) -> bool:
    '''
    判断该账号是否已存在
    Args:
        account: 用户账号

    Returns:
        True；存在
        False：不存在
    '''

    # 判断是否存在
    if account in BANK_INFO:
        return True
    return False


def is_full() -> bool:
    '''
    判断银行用户是否已满
    Returns:
        True：已满
        False：未满
    '''
    # 获取银行信息
    metadata = BANK_INFO.get("metadata")
    # 判断是否达到最大用户量
    return metadata.get("user_count") >= metadata.get("max_user_count")


def add_account(name: str, password: int, money: int, address: dict) -> int:
    '''
    添加用户
    Args:
        name: 姓名
        password: 密码
        address: 地址
        money: 开户金额

    Returns:
        0：成功
        2：账号已存在
        3：用户库已满
    '''

    # 随机账号
    account = ''.join(random.choices(string.digits, k=8))
    # 获取银行信息
    bank_metadata = BANK_INFO.get("metadata")

    # 账号已存在
    if is_exist(account):
        return 2
    # 用户库已满
    if is_full():
        return 3

    # 创建账号，添加账号信息
    BANK_INFO[account] = {
        "name": name,
        "password": password,
        "address": address,
        "balance": money,
        "opening_bank": bank_metadata.get("bank_name")
    }
    # 更新银行信息
    bank_metadata["user_count"] += 1
    print("您的账号为: ", account)
    return 0


def save_money(account: str, money: int) -> int:
    '''
    账号存钱
    Args:
        account: 用户账号
        money: 存款金额

    Returns:
        0：存款成功
        1：账号不存在
    '''

    # 账号存在则存款
    if is_exist(account):
        # 获取账号信息
        user = BANK_INFO.get(account)
        # 金额增加
        user["balance"] += money
        return 0
    # 账号不存在
    return 1


def get_money(account: str, password: int, money: int) -> int:
    '''
    账号取钱
    Args:
        account: 用户账号
        password: 用户密码
        money: 取款金额

    Returns:
        0：成功
        1：账号不存在
        4：密码错误
        5：余额不足
    '''

    # 账号存在
    if is_exist(account):
        # 获取账号用户
        user = BANK_INFO.get(account)
        # 密码正确
        if user.get("password") == password:
            # 余额足够
            if user.get("balance") >= money:
                user["balance"] -= money
                # 存款成功
                return 0
            # 余额不足
            return 5
        # 密码错误
        return 4
    # 账号不存在
    return 1


def transfer_money(account: str, password: int, in_account: str, money: int) -> int:
    '''
    用户转账
    Args:
        account: 转出用户账号
        password: 转出用户密码
        in_account: 转入用户账号
        money: 转账金额

    Returns:
        0：正常
        1：账号不存在
        4：密码错误
        5：金额不足
    '''

    # 账号存在
    if is_exist(account) and is_exist(in_account):
        out_user = BANK_INFO.get(account)
        in_user = BANK_INFO.get(in_account)
        # 密码正确
        if out_user.get("password") == password:
            # 余额足够
            if out_user.get("balance") >= money:
                out_user["balance"] -= money
                in_user["balance"] += money
                # 转账成功
                return 0
            # 余额不足
            return 5
        # 密码错误
        return 4
    # 账号不存在
    return 1


def search_account(account: str, password: int) -> int:
    '''
    查询用户信息
    Args:
        account: 用户账号
        password: 用户密码

    Returns:
        0：正常
        1：账号不存在
        4：密码错误
    '''

    # 账号存在
    if is_exist(account):
        # 获取账号
        user = BANK_INFO.get(account)
        # 判断账号密码
        if user.get("password") == password:
            # 输出用户信息
            print_account(account)
            return 0
        return 4
    return 1


def ICBC_main():
    print("欢迎使用！")
    while True:
        # 输出操作选项菜单
        print_menu()
        # 接收选项值
        option = input_int("请输入操作选项: ")

        # 开户操作
        if option == 1:
            # 收集用户输入数据
            data = input_user(name=True, password=True, money=True, address=True)
            # 开户
            code = add_account(**data)
            # 输出操作信息
            print_err(code)
            # 账号重复处理
            if code == ERR_CODE.get("AC_EXISTED"):
                print("正在重新创建中...")
                while code == ERR_CODE.get("AC_EXISTED"):
                    code = add_account(**data)

        # 存款操作
        elif option == 2:
            # 收集用户输入数据
            data = input_user(account=True, money=True)
            # 存款
            code = save_money(**data)
            # 输出操作信息
            print_err(code)

        # 取钱操作
        elif option == 3:
            # 收集用户输入数据
            data = input_user(account=True, password=True, money=True)
            # 取款
            code = get_money(**data)
            # 输出操作信息
            print_err(code)

        # 转账操作
        elif option == 4:
            # 收集用户输入数据
            data = input_user(account=True, password=True, money=True, in_account=True)
            # 转账
            code = transfer_money(**data)
            # 输出操作信息
            print_err(code)

        # 查询操作
        elif option == 5:
            # 收集用户输入数据
            data = input_user(account=True, password=True)
            # 查询
            code = search_account(**data)
            # 输出操作信息
            print_err(code)

        # 退出操作
        elif option == 6:
            return

        # 未知选项
        else:
            print("非法选项！")


if __name__ == '__main__':
    ICBC_main()
